/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
 * Description: Source file for parameter and string processing.
 * Author: -
 * Create: 2021.4.19
 */

#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "udk_common.h"
#include "udk_args.h"

int udk_strsplit(char *string, int stringlen, char **tokens, int maxtokens, char delim)
{
    int i, tok = 0;
    int tokstart = 1;

    if (string == NULL || tokens == NULL) {
        errno = EINVAL;
        return -1;
    }

    for (i = 0; i < stringlen; i++) {
        if (string[i] == '\0' || tok >= maxtokens) {
            break;
        }
        if (tokstart != 0) {
            tokstart = 0;
            tokens[tok++] = &string[i];
        }
        if (string[i] == delim) {
            string[i] = '\0';
            tokstart = 1;
        }
    }
    return tok;
}

void udk_kvargs_free(struct udk_kvargs *kvlist)
{
    if (kvlist == NULL) {
        return;
    }
    free(kvlist->str);
    free(kvlist);
}

static int is_valid_key(const char * const valid_keys[], const char *key)
{
    const char * const * ptr;
    for (ptr = valid_keys; *ptr != NULL; ++ptr) {
        if (strcmp(key, *ptr) == 0) {
            return 1;
        }
    }
    return 0;
}

static int udk_check_for_valid_keys(struct udk_kvargs *kvlist, const char * const valid_keys[])
{
    uint32_t i;

    for (i = 0; i < kvlist->count; ++i) {
        if (!is_valid_key(valid_keys, kvlist->pairs[i].key)) {
            return -1;
        }
    }
    return 0;
}

/*
 * parse a string and insert into kvlist.
 * string: "key1=value1,key2=value2,key3=value3.....".
 */
static int udk_kvargs_tokenize(struct udk_kvargs *kvlist, const char *args)
{
    char *str = NULL;
    char *ctx_outer = NULL;
    char *ctx_inner = NULL;
    uint32_t i;

    kvlist->str = strdup(args);
    if (!kvlist->str) {
        return -1;
    }

    str = kvlist->str;
    while ((str = strtok_r(str, UDK_KVARGS_PAIRS_DELIM, &ctx_outer)) != NULL) {
        i = kvlist->count;
        if (i >= UDK_KVARGS_MAX) {
            return -1;
        }

        kvlist->pairs[i].key = strtok_r(str, UDK_KVARGS_KV_DELIM, &ctx_inner);
        kvlist->pairs[i].value = strtok_r(NULL, UDK_KVARGS_KV_DELIM, &ctx_inner);
        if (!kvlist->pairs[i].key || !kvlist->pairs[i].value) {
            return -1;
        }

        /* if value has format as "[a, b]", need to skip the comma in bracket */
        str = kvlist->pairs[i].value;
        if (str[0] != '[') {
            kvlist->count++;
            str = NULL;
            continue;
        }
        
        while (str[strlen(str) - 1] != ']') {
            if (ctx_outer == NULL || ctx_outer[0] == '\0') {
                return -1;
            }
            str[strlen(str)] = ',';
            str = strtok_r(NULL, UDK_KVARGS_PAIRS_DELIM, &ctx_outer);
            if (str == NULL) {
                return -1;
            }
        }

        kvlist->count++;
        str = NULL;
    }

    return 0;
}

uint32_t udk_kvargs_count(const struct udk_kvargs *kvlist, const char *key_match)
{
    uint32_t i;
    uint32_t count = 0;

    for (i = 0; i < kvlist->count; ++i) {
        if (key_match == NULL || !strcmp(kvlist->pairs[i].key, key_match)) {
            count++;
        }
    }

    return count;
}

struct udk_kvargs *udk_kvargs_parse(const char *args, const char * const valid_keys[])
{
    struct udk_kvargs *kvlist = (struct udk_kvargs *)calloc(1, sizeof(struct udk_kvargs));
    if (kvlist == NULL) {
        return NULL;
    }

    if (udk_kvargs_tokenize(kvlist, args) < 0) {
        udk_kvargs_free(kvlist);
        return NULL;
    }

    if (valid_keys != NULL && udk_check_for_valid_keys(kvlist, valid_keys) != 0) {
        udk_kvargs_free(kvlist);
        return NULL;
    }

    return kvlist;
}

int udk_kvargs_process(const struct udk_kvargs *kvlist, const char *key_match, arg_handler_t handler, void *opaque_arg)
{
    uint32_t i;

    if (kvlist == NULL) {
        return 0;
    }

    for (i = 0; i < kvlist->count; ++i) {
        if (key_match == NULL || !strcmp(kvlist->pairs[i].key, key_match)) {
            if ((*handler)(kvlist->pairs[i].key, kvlist->pairs[i].value, opaque_arg) < 0) {
                return -1;
            }
        }
    }

    return 0;
}
